/*
 * Copyright (C) 2008 Chris Bellew
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#import <UIKit/UIKit.h>
#import <UIKit/UIHardware.h>
#import "iDynDNSSettings.h"

#import <sys/socket.h>   // for AF_INET, PF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR
#import <sys/types.h>
#import <netinet/in.h>   // for IPPROTO_TCP, sockaddr_in
#import <arpa/inet.h>       // for inet_ntoa
#import <ifaddrs.h>

static const char encodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static iDynDNSSettings *_sharedInstance = nil;

#define VERSION @"1.0"

@implementation iDynDNSSettings
	
+ (iDynDNSSettings *)sharedInstance
{
	if(!_sharedInstance) {
		_sharedInstance = [[iDynDNSSettings alloc] init];
	}
    return _sharedInstance;
}

-(id) init {
	_sharedInstance = self;
	
	[self getCurrentIP];
	
	_statusCodes = [[NSDictionary alloc] initWithObjectsAndKeys: 
			@"Update Successful.", @"good", 
			@"No settings changed. Additional updates like this will cause the hostname to be blocked.", @"nochg", 
			@"Login incorrect.", @"badauth", 
			@"Hostname not a fully-qualified domain name.", @"notfqdn", 
			@"Hostname does not exist in this user account.", @"nohost", 
			@"Hostname is blocked for abuse.", @"abuse", 
			@"DNS error occurred.", @"dnserr", 
			@"There is a problem or scheduled maintenance on dyndns.com.", @"911",
			nil];
		
	_username = nil;
	_password = nil;
	_hostname = nil;
	
	return self;
}

- (void) dealloc {
	[_username release];
	[_password release];
	[_hostname release];
	[_lastip release];
	[_updateInterval release];
	[_localAddress release];
	[_statusCodes release];
	[_lastUpdateDate release];
	[[iDynDNSSettings sharedInstance] release];
	[super dealloc];
}

- (NSString *)username {
    if (_username == nil) {
        return @"";
    }
    return _username;
}

-(void)setUsername:(NSString*)username {
	if (_username) {
        [_username release];
    }
    _username = [username retain];
}

- (NSString *)password {
    if (_password == nil) {
        return @"";
    }
    return _password;
}

-(void)setPassword:(NSString*)password {
	if (_password) {
        [_password release];
    }
    _password = [password retain];
}

- (NSString *)hostname {
    if (_hostname == nil) {
        return @"";
    }
    return _hostname;
}

-(void)setHostname:(NSString*)hostname {
	if (_hostname) {
        [_hostname release];
    }
    _hostname = [hostname retain];
}

- (NSString *)lastip {
    if (_lastip == nil) {
        return @"";
    }
    return _lastip;
}

-(void)setLastIP:(NSString*)lastip {
	if (_lastip) {
        [_lastip release];
    }
    _lastip = [lastip retain];
}

- (NSNumber *)updateInterval {
    if (_updateInterval == nil) {
        return [NSNumber numberWithInt:5];
    }
    return _updateInterval;
}

-(void)setUpdateInterval:(NSNumber*)updateInterval {
	if (_updateInterval) {
        [_updateInterval release];
    }
    _updateInterval = [updateInterval retain];
}

- (NSDate*)lastUpdateDate {
	return _lastUpdateDate;
}

- (void)setLastUpdateDate:(NSDate*)lastUpdateDate {
	_lastUpdateDate = [lastUpdateDate retain];
}

- (NSString *)localAddress {
	if(_localAddress == nil) {
		return @"";
	}
    return _localAddress;
}

- (BOOL)autoUpdate {
	if(_autoUpdate == nil) {
		_autoUpdate = NO;
	}
	return _autoUpdate;
}

- (void)setAutoUpdate:(BOOL)autoUpdate {
	if(autoUpdate == nil) {
		autoUpdate = NO;
	}
	_autoUpdate = autoUpdate;
}

- (void)saveSettings {
//	[[MobileCastController sharedInstance] openProgressHUD:@"Syncing With PodGrove.com"];
	
    NSDictionary *feedDict;
    NSString *userDefaultsFile;
    BOOL isDirectory;
    
    userDefaultsFile = [[[self userLibraryDirectory] stringByAppendingPathComponent:@"Preferences"] stringByAppendingPathComponent:@"com.bantha-fodder.idyndns.plist"];
    
    feedDict = [NSDictionary dictionaryWithObjectsAndKeys:[self username], @"username", [self password], @"password", [self hostname], @"hostname", [self lastip], @"lastip", [self updateInterval], @"updateinterval", [NSNumber numberWithBool:[self autoUpdate]], @"autoupdate", [self lastUpdateDate], @"lastupdatedate", nil];
	
    if ( [[NSFileManager defaultManager] fileExistsAtPath:userDefaultsFile isDirectory:&isDirectory] && !isDirectory ) {
        [[NSFileManager defaultManager] removeFileAtPath:userDefaultsFile handler:nil];
    }
    if ( ![feedDict writeToFile:userDefaultsFile atomically:YES] ) {
        NSLog(@"Could not write defaults");
    }
}

-(void)loadSettings {
	NSDictionary *defaults;
    NSString *userDefaultsFile;

    userDefaultsFile = [[[self userLibraryDirectory] stringByAppendingPathComponent:@"Preferences"] stringByAppendingPathComponent:@"com.bantha-fodder.idyndns.plist"];
    NSDictionary* dict = [NSDictionary dictionaryWithContentsOfFile:userDefaultsFile];
	[self setUsername:[dict valueForKey:@"username"]];
	[self setPassword:[dict valueForKey:@"password"]];
	[self setHostname:[dict valueForKey:@"hostname"]];
	[self setLastIP:[dict valueForKey:@"lastip"]];
	[self setUpdateInterval:[dict valueForKey:@"updateinterval"]];
	NSNumber* aupdate = [dict valueForKey:@"autoupdate"];
	if (aupdate == nil) {
        _autoUpdate = NO;
    } else {
    	_autoUpdate = [aupdate boolValue];
	}
	[self setAutoUpdate:_autoUpdate];
	[self setLastUpdateDate:[dict valueForKey:@"lastupdatedate"]];
}

-(void)getCurrentIP {
	struct ifaddrs * ifa;
	
	if ( getifaddrs( & ifa ) ) {
		_localAddress = [@"Unknown" copy];
	} else {
		struct ifaddrs * ifp = ifa;
		while( ifp ) {
			if ( ! strcmp( ifp->ifa_name , "en0" ) ) {
				if ( ifp->ifa_addr->sa_family == AF_INET ) {
					struct sockaddr_in * sin = (struct sockaddr_in *) ifp->ifa_addr;
					char * address = inet_ntoa( sin->sin_addr );
					_localAddress = [[NSString stringWithCString:address] copy];
				}
			}
			ifp = ifp->ifa_next;
		}	
		freeifaddrs( ifa );
	}
}

-(NSDictionary*)statusCodes {
	return _statusCodes;
}

-(NSString*)updateIP {
	if(!_updateInProgress) {
		_updateInProgress = YES;
		// Assemble the url to send to dyndns.org
		NSString *url = [NSString stringWithFormat:@"http://members.dyndns.org/nic/update?hostname=%@&myip=%@&wildcard=OFF&backmx=NO", _hostname, _localAddress];
		NSURL *nsurl = [NSURL URLWithString:url];
		NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:nsurl cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.0];
		
		NSString *authString = [NSString stringWithFormat:@"%@:%@", _username, _password];
		[request addValue:[NSString stringWithFormat:@"Basic %@", [self base64Encoding:authString]] forHTTPHeaderField:@"Authorization"];
		[request addValue:[NSString stringWithFormat:@"iDynDNS/%@ (iPhone)", VERSION] forHTTPHeaderField:@"User-Agent"];
		NSURLResponse *resp = nil;
		NSError *err = nil;
		NSData *response = [NSURLConnection sendSynchronousRequest: request returningResponse: &resp error: &err];
		NSString *theString = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding]; 
		NSMutableString *statusCode = [NSMutableString stringWithFormat:@"%@", theString];
		[statusCode replaceOccurrencesOfString:[@" " stringByAppendingString:[self localAddress]] withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [theString length])];
		
		if([statusCode isEqualToString:@"good"] || [statusCode isEqualToString:@"nochg"]) {
			NSLog(@"Update successful, saving");
			[self setLastIP:[self localAddress]];
			[self setLastUpdateDate:[NSDate date]];
			[self saveSettings];
		}
		NSLog(@"Status code = %@", statusCode);
		//[self performSelector:@selector(blahblah) withObject:nil afterDelay:1];
		_updateInProgress = NO;
		return statusCode;
	}
	return @"";
}

// Convert a string to base64
- (NSString *)base64Encoding:(NSString*)string
{
	NSData *str = [NSData dataWithBytes:[string cString] length:[string cStringLength]];
	if ([str length] == 0)
		return @"";

    char *characters = malloc((([str length] + 2) / 3) * 4);
	if (characters == NULL)
		return nil;
	int length = 0;
	
	int i = 0;
	while (i < [str length])
	{
		char buffer[3] = {0,0,0};
		short bufferLength = 0;
		while (bufferLength < 3 && i < [str length])
			buffer[bufferLength++] = ((char *)[str bytes])[i++];
		
		//  Encode the bytes in the buffer to four characters, including padding "=" characters if necessary.
		characters[length++] = encodingTable[(buffer[0] & 0xFC) >> 2];
		characters[length++] = encodingTable[((buffer[0] & 0x03) << 4) | ((buffer[1] & 0xF0) >> 4)];
		if (bufferLength > 1)
			characters[length++] = encodingTable[((buffer[1] & 0x0F) << 2) | ((buffer[2] & 0xC0) >> 6)];
		else characters[length++] = '=';
		if (bufferLength > 2)
			characters[length++] = encodingTable[buffer[2] & 0x3F];
		else characters[length++] = '=';	
	}
	
	return [[[NSString alloc] initWithBytesNoCopy:characters length:length encoding:NSASCIIStringEncoding freeWhenDone:YES] autorelease];
}



@end
